#ifndef __YUECHUAN_SCHEDULER_H__
#define __YUECHUAN_SCHEDULER_H__

#include <memory>
#include <utility>
#include <vector>
#include<list>
#include<atomic>
#include "thread.h"
#include "fiber.h"

namespace yuechuan {
    /**
     * @brief 协程调度器
     * @details 用于调度协程，其中有一个root协程，其他协程的创建，关闭都要经过该协程
     */
    class Scheduler {
    public:

        typedef std::shared_ptr<Scheduler> ptr;
        typedef Mutex MutexType;

        /**
         * @brief 构造函数
         * @param count 线程池中线程的数量
         * @param use_caller 是否使用当前调用线程, 是否将创建协程调度器的线程放到协程调度器中的线程池中
         * @param name 协程调度器名称
         */
        Scheduler(size_t count = 1, bool use_caller = true, std::string name = "UNKNOW");

        /**
         * @brief 析构函数
         */
        virtual ~Scheduler();

        /**
         * @brief 返回当前协程调度器名称
         * @return std::string
         */
        const std::string getName() const;

        /**
         * @brief 返回当前协程调度器
         * @return Schedule*
         */
        static Scheduler *GetThis();

        /**
         * @brief 返回当前协程调度器中root协程，也就是调度协程
         * @return Fiber*
         */
        static Fiber *GetMainFiber();

        /**
         * @brief 启动协程调度器
         */
        void start();

        /**
         * @brief 停止协程调度器
         */
        void stop();

        /**
         * @brief 调度协程
         * @tparam FiberOrFunc 协程或者函数类
         * @param fc   协程或者函数
         * @param thread  协程执行的线程ID， -1表示任意线程
         */
        template<class FiberOrFunc>
        void schedule(FiberOrFunc fc, int thread = -1) {
            bool need_tickle = false;
            {
                MutexType::Lock lock(m_mutex);
                need_tickle = scheduleNoLock(fc, thread);
            }
            if (need_tickle) {
                tickle();
            }

        }

        /**
         * @brief 批量调度协程
         * @tparam InputIterator 输入迭代器
         * @param begin 协程数组的开始
         * @param end  协程数组的结束
         */
        template<class InputIterator>
        void schedule(InputIterator begin, InputIterator end) {
            bool need_tickle = false;
            {
                MutexType::Lock lock(m_mutex);
                while (begin != end) {
                    need_tickle = scheduleNoLock(&*begin, -1) || need_tickle;
                    ++begin;
                }
            }
            if (need_tickle) {
                tickle();
            }
        }

    protected:
        /**
         * @brief 提醒调度器有任务
         */
        virtual void tickle();

        /**
         * @brief 协程调度函数
         * @details 协程调度器创建线程从任务队列中取得任务，然后进行调度
         *          当等待队队列为空时，协程调度进入idle轮询协程执行
         */
        void run();

        /**
         * @brief 是否可以停止
         * @return bool
         */
        virtual bool stopping();

        /**
         * @brief 协程调度器无可调度的协程时执行idle协程
         * @details 当前协程调度器，没有需要调度的任务，又不能使线程终止
         */
        virtual void idle();

        /**
         * @brief 设置当前的协程调度器
         */
        void setThis();

        /**
         * @brief 是否有空闲线程
         * @return bool
         */
        bool hasIdleCount() { return m_idleThreadCount > 0; }

    private:
        /**
         * @brief 协程调度启动(无锁)
         * @details 当放入一个协程或者函数时，就尝试提醒调度器，让其调度
         * @tparam FiberOrFunc
         * @param fc 协程或函数
         * @param thread 协程所在线程ID，-1表示任意线程
         * @return bool 是否需要通知
         */
        template<class FiberOrFunc>
        bool scheduleNoLock(FiberOrFunc fc, int thread = -1) {    // 不加锁的操作，上面的函数要对m_fibers进行写操作，需要在插入前添加锁
            // 但锁后的操作是统一的，将其抽象出来
            bool need_tickle = m_fibers.empty();
            Task ft(fc, thread);
            if (ft.cb || ft.fiber) {
                m_fibers.push_back(ft);
            }
            return need_tickle;
        }

    private:
        /**
         * @brief 协程/函数/线程组
         */
        struct Task {
            Fiber::ptr fiber;                        /// 可能是一个协程
            std::function<void()> cb;                /// 也可能直接传入一个函数执行
            int thread;                                /// 线程ID，调度器可以指定该协程或者方法在那个线程中执行
            /**
             * @brief 构造函数
             * @param f 协程智能指针
             * @param thr 线程ID
             */
            Task(Fiber::ptr f, int thr)
                    : thread(thr) {
                fiber = std::move(f);
            }

            /**
             * @brief 构造函数
             * @param f 协程指针
             * @param thr 线程ID
             */
            Task(Fiber::ptr *f, int thr)
                    : thread(thr) {     /// 用于智能指针的释放问题，传入一个智能指针，在函数中调用swap可以将传入的智能指针的引用值减一
                fiber.swap(*f);
            }

            /**
             * @brief 构造函数
             * @param f 协程执行函数
             * @param thr 线程ID
             */
            Task(std::function<void()> f, int thr)
                    : thread(thr) {
                cb = std::move(f);
            }

            /**
             * @brief 构造函数
             * @param f 协程执行函数指针
             * @param thr 线程ID
             */
            Task(std::function<void()> *f, int thr)
                    : thread(thr) {
                cb.swap(*f);
            }

            /**
             * @brief 无参构造函数
             * @brief 创建一个空协程，指定线程ID为任意线程
             */
            Task()
                    : thread(-1) {}

            /**
             * @brief 重置数据
             */
            void reset() {
                cb = nullptr;
                fiber = nullptr;
                thread = -1;
            }
        };

    private:
        /// 互斥量
        MutexType m_mutex;
        /// 线程池
        std::vector<Thread::ptr> m_threads;
        /// 协程调度器名称
        std::string m_name;
        /// 待执行的协程队列，可传入函数或者协程对象指针
        std::list<Task> m_fibers;
        /// 主协程
        Fiber::ptr m_rootFiber;
    protected:
        /// 线程ID数组
        std::vector<int> m_threadIds;
        /// 线程个数
        size_t m_threadCount = 0;
        /// 工作线程个数
        std::atomic<size_t> m_activateThreadCount = {0};
        /// 空闲线程个数
        std::atomic<size_t> m_idleThreadCount = {0};
        /// 是否正在停止
        bool m_stopping = true;
        /// 是否自动停止
        bool m_autoStop = false;
        /// 主线程号
        int m_rootThread = -1;


    };
} // namespace yuechuan
#endif